package cn.cxyxj.study06.email;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author cxyxj
 */
public class EmailVerificationCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    /**
     * 默认的请求参数名称
     */
    public static final String EMAIL_KEY = "email";
    public static final String EMAIL_CODE_KEY = "emailCode";

    private String emailParameter = EMAIL_KEY;
    private String emailCodeParameter = EMAIL_CODE_KEY;
    /**
     * 是否仅支持post方式
     */
    private boolean postOnly = true;


    /**
     * 对请求进行过滤，只有接口为 /emil-login，请求方式为 POST，才会进入逻辑
     */
    public EmailVerificationCodeAuthenticationFilter() {
        super(new AntPathRequestMatcher("/email-login", "POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        // 需要是 POST 请求
        if (postOnly &&  !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        // 判断请求格式是否 JSON
        if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
            Map<String, String> loginData = new HashMap<>(2);
            try {
                loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class);
            } catch (IOException e) {
                throw new InternalAuthenticationServiceException("请求参数异常");
            }
            // 获得请求参数
            String email = loginData.get(emailParameter);
            String emailCode = loginData.get(emailCodeParameter);
            // 检查验证码
            checkEmailCode(emailCode);
            if(StringUtils.isEmpty(email)){
                throw new AuthenticationServiceException("邮箱不能为空");
            }
            /**
             * 使用请求参数传递的邮箱和验证码，封装成 EmailVerificationCodeAuthenticationToken 对象，
             * 然后将该对象交给 provider 进行授权
             */
            EmailVerificationCodeAuthenticationToken authRequest = new EmailVerificationCodeAuthenticationToken(email);
            setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
        return null;
    }

    public void setDetails(HttpServletRequest request , EmailVerificationCodeAuthenticationToken token){
        token.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }


    private void checkEmailCode(String emailCode) {
        // 实际当中请从 Redis 中获取
        String verifyCode = "123456";
        if(StringUtils.isEmpty(verifyCode)){
            throw new AuthenticationServiceException("请重新申请验证码!");
        }
        if (!verifyCode.equalsIgnoreCase(emailCode)) {
            throw new AuthenticationServiceException("验证码错误!");
        }
    }
}
